<?php
/**
 * Form Plugin: helps to create forms without allowing html
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Esther Brunner <wikidesign@gmail.com>  
 */
 
if (!defined('DOKU_INC')) die();

if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");

require_once(DOKU_PLUGIN.'syntax.php');
 
class syntax_plugin_form extends DokuWiki_Syntax_Plugin {
  
  function getInfo(){
    return array(
      'author' => 'Esther Brunner',
      'email'  => 'wikidesign@gmail.com',
      'date'   => '2007-03-15',
      'name'   => 'Form Plugin',
      'desc'   => 'helps to create forms without allowing html',
      'url'    => 'http://www.wikidesign.ch/en/plugin/form/start',
    );
  }

  function getType(){ return 'substition';}
  function getPType(){ return 'block';}
  function getSort(){ return 169; }
  
  function connectTo($mode){
    $this->Lexer->addSpecialPattern('<\w*?form.*?>.+?</form>', $mode, 'plugin_form');
  }

  function handle($match, $state, $pos, &$handler){
    $match = substr($match, 1, -6);  // strip markup
    list($action, $options) = explode('>', $match, 2);
    list($action, $address) = preg_split('/form/u', $action, 2);
    list($address, $title) = explode('|', $address, 2);

    $options = explode("\n", $options);
    $result  = array();
    foreach ($options as $option){
      if (!trim($option)) continue; // ignore empty lines
      if (($option{0} == '^') || ($option{0} == '|')){
        list($label, $input) = explode('|', substr($option, 1, -1), 2);
        $class = ($option{0} == '^' ? 'th' : 'td' );
      } else {
        $input = $option;
        $label = '';
        $class = NULL;
      }
      $it = $this->_handleInput(trim($input), trim($label), $class);
      if ($it) $result[] = $it;
    }
    
    // there must be a file $action.php in the form plugin folder
    if (!$action || !@file_exists(DOKU_PLUGIN.'form/'.$action.'.php'))
      $action = $this->getConf('formscript');
        
    return array($action, trim($address), trim($title), $result);
  }
  
  /**
   * Handles various input types:
   *
   * /* hidden -> value *_/ (without the underscore in the closing markup)
   * [_ text -> default _]
   * [_ textarea -> default \\ \\ \\ _] (4 lines)
   * [* password *]
   * {_ file _}
   * (: select -> option1, option2, ... :)
   * (* radio -> value1, value2, ... *)
   * (- checkbox -> value -)
   * (! submit -> button !)
   * (. cancel -> button .)
   */
  function _handleInput($input, $label = '', $class = NULL){
    switch ($input{0}){
      case '/':
        if (substr($input, 0, 2) == '/*'){ // hidden input field
          list($name, $value) = explode('->', substr($input, 2, -2));
          return array(
            'type'  => 'hidden',
            'name'  => trim($name),
            'value' => trim($value),
          );
        } else {
          return NULL;
        }
      case '[':
        if (substr($input, 0, 2) == '[_'){ // text or textarea input field
          $n = substr_count($input, '\\');
          if ($n > 0){
            $input = trim(str_replace('\\', '', $input));
            $type = 'textarea';
            $rows = $n + 1;
          } else {
            $type = 'text';
            $rows = NULL;
          }
          list($name, $value) = explode('->', substr($input, 2, -2));
          return array(
            'type'  => $type,
            'label' => $label,
            'class' => $class,
            'name'  => trim($name),
            'value' => trim($value),
            'rows'  => $rows,
          );
        } elseif (substr($input, 0, 2) == '[*'){ // password input field
          return array(
            'type'  => 'password',
            'label' => $label,
            'class' => $class,
            'name'  => trim(substr($input, 2, -2)),
          );
        } else {
          return NULL;
        }
      case '{':
        if (substr($input, 0, 2) == '{_'){ // file upload field and button
          return array(
            'type'  => 'file',
            'label' => $label,
            'class' => $class,
            'name'  => trim(substr($input, 2, -2)),
          );
        } else {
          return NULL;
        }
      case '(':
        list($name, $value) = explode('->', substr($input, 2, -2));
        $value = trim($value);
        switch ($input{1}){
          case '!':
            $type  = 'submit'; // submit button
            break;
          case '.':
            $type  = 'reset'; // reset button
            break;
          case ':':
            $type  = 'select'; // popup menu with options
            $value = explode(',', $value);
            break;
          case '*':
            $type  = 'radio'; // radio button (group)
            $value = explode(',', $value);
            break;
          case '-':
            $type  = 'checkbox'; // checkbox(es group)
            break;
          default:
            return NULL;
        }
        return array(
          'type'  => $type,
          'label' => $label,
          'class' => $class,
          'name'  => trim($name),
          'value' => $value,
        );
      default:
        return NULL;
    }
  }

  function render($mode, &$renderer, $data) {
  
    if ($mode == 'xhtml'){
      global $conf;
    
      list($action, $address, $title, $inputs) = $data;
      
      // security: check if it is a valid action
      $validactions = array('mail', 'newpage');
      if (!in_array($action, $validactions)) return false;
      
      // an array of elements for which we don't want labels
      $nolabels = array('hidden', 'submit', 'reset');
      $thanks = $this->getConf('thankspage');
      
      // output the form
      $renderer->doc .= '<form method="post"'.
        ' action="'.DOKU_BASE.'lib/plugins/form/'.$action.'.php"'.
        ' accept-charset="utf-8" enctype="multipart/form-data">'.DOKU_LF;
      if ($title) $renderer->doc .= DOKU_TAB.'<fieldset style="width: 80%">'.DOKU_LF.
        DOKU_TAB.DOKU_TAB.'<legend>'.hsc($title).'</legend>'.DOKU_LF;
      
      // form to send mail, recipient given (subject from field "subject")
      if ($action == 'mail'){
        $renderer->doc .= $this->_hidden(array('name' => 'to', 'value' => hsc($address))).
          $this->_hidden(array('name' => 'DOKU_DO', 'value' => 'show')).
          $this->_hidden(array('name' => 'DOKU_ID', 'value' => $thanks));
          
      // form to create new page, namespace given (title from field "title")
      } elseif ($action == 'newpage'){
        $renderer->doc .= $this->_hidden(array('name' => 'ns', 'value' => hsc($address))).
          $this->_hidden(array('name' => 'do', 'value' => 'show'));
      }
      
      foreach ($inputs as $input){
        $div   = (!in_array($input['type'], $nolabels));
        $label = ($input['label'] && $div);
        
        if ($label){
          $renderer->doc .= DOKU_TAB.DOKU_TAB.'<label class="'.$input['class'].'">'.hsc($input['label']).'</label>'.DOKU_LF.
          DOKU_TAB.DOKU_TAB.'<div class="form__column">'.DOKU_LF;
        } elseif ($div){
          $renderer->doc .= DOKU_TAB.DOKU_TAB.'<div class="form__wide">'.DOKU_LF;
        }
        $function = '_'.$input['type'];
        $renderer->doc .= $this->$function($input);
        if ($div) $renderer->doc .= DOKU_TAB.DOKU_TAB.'</div>'.DOKU_LF;
      }
      if ($title) $renderer->doc .= DOKU_TAB.'</fieldset>'.DOKU_LF;
      $renderer->doc .= '</form>'.DOKU_LF;
          
      return true;
    }
    return false;
  }
  
  /**
   * Returns the XHTML for a hidden input field
   */
  function _hidden($input){
    return DOKU_TAB.DOKU_TAB.'<input type="hidden" name="'.hsc($input['name']).'" value="'.hsc($input['value']).'" />'.DOKU_LF;
  }
  
  /**
   * Returns the XHTML for a text input field
   */
  function _text($input){
    $ret = DOKU_TAB.DOKU_TAB.DOKU_TAB.'<input type="text" name="'.hsc($input['name']).'" class="edit" size="50"';
    if ($input['value']) $ret .= ' value="'.hsc($input['value']).'"';
    $ret .= ' />'.DOKU_LF;
    return $ret;
  }

  /**
   * Returns the XHTML for a textarea input field
   */
  function _textarea($input){
    $ret = DOKU_TAB.DOKU_TAB.DOKU_TAB.'<textarea name="'.$input['name'].'" class="edit" cols="50" rows="'.$input['rows'].'">';
    if ($input['value']) $ret .= hsc($input['value']);
    $ret .= '</textarea>'.DOKU_LF;
    return $ret;
  }

  /**
   * Returns the XHTML for a password input field
   */
  function _password($input){
    return DOKU_TAB.DOKU_TAB.DOKU_TAB.'<input type="password" name="'.hsc($input['name']).'" class="edit" size="50" />'.DOKU_LF;
  }

  /**
   * Returns the XHTML for a file upload field and button
   */
  function _file($input){
    return DOKU_TAB.DOKU_TAB.DOKU_TAB.'<input type="file" name="'.hsc($input['name']).'" />'.DOKU_LF;
  }

  /**
   * Returns the XHTML for a select element with options
   */
  function _select($input){
    $ret = DOKU_TAB.DOKU_TAB.DOKU_TAB.'<select name="'.hsc($input['name']).'" size="1" />'.DOKU_LF;
    foreach ($input['value'] as $option){
      $option  = trim($option);
      $trimmed = trim($option, '_');
      if ($option != $trimmed) $selected = ' selected="selected"';
      else $selected = '';
      $ret .= DOKU_TAB.DOKU_TAB.DOKU_TAB.DOKU_TAB.'<option'.$selected.'>'.hsc($trimmed).'</option>'.DOKU_LF;
    }
    $ret .= DOKU_TAB.DOKU_TAB.DOKU_TAB.'</select>'.DOKU_LF;
    return $ret;
  }

  /**
   * Returns the XHTML for a radio button (group)
   */
  function _radio($input){
    $ret   = array();
    $start = DOKU_TAB.DOKU_TAB.DOKU_TAB.'<input type="radio" name="'.hsc($input['name']).'" value="';
    $middle = '" /> ';
    foreach ($input['value'] as $value){
      $value   = trim($value);
      $trimmed = trim($value, '_');
      if ($value != $trimmed) $checked = '" checked="checked';
      else $checked = '';
      $ret[] = $start.hsc($trimmed).$checked.$middle.hsc($trimmed);
    }
    return join('<br />'.DOKU_LF, $ret).DOKU_LF;
  }

  /**
   * Returns the XHTML for a checkbox(es group)
   */
  function _checkbox($input){
    $value   = trim($input['value']);
    $trimmed = trim($value, '_');
    if ($value != $trimmed) $checked = '" checked="checked';
    else $checked = '';
    return DOKU_TAB.DOKU_TAB.DOKU_TAB.'<input type="checkbox" name="'.hsc($input['name']).'" value="'.hsc($trimmed).$checked.'" /> '.hsc($trimmed).DOKU_LF;
  }

  /**
   * Returns the XHTML for a submit button
   */
  function _submit($input){
    return DOKU_TAB.DOKU_TAB.'<input type="submit" name="'.hsc($input['name']).'" value="'.hsc($input['value']).'" class="button" />'.DOKU_LF;
  }

  /**
   * Returns the XHTML for a reset button
   */
  function _reset($input){
    return DOKU_TAB.DOKU_TAB.'<input type="reset" name="'.hsc($input['name']).'" value="'.hsc($input['value']).'" class="button" />'.DOKU_LF;
  }
      
}
 
//Setup VIM: ex: et ts=4 enc=utf-8 :
